/*
 * sender.c
 *
 *  Created on: 2013. 10. 16.
 *      Author: jphong
 */

#include "np.h"
#include "packet.h"

typedef enum {
	Wait_for_call, Wait_for_ACK, Closing
} State;

extern int DEBUG_ON;	// print debug messages if set
static unsigned int ReTxTimeout = 100;		// default timeout in msec
static char *localhost = "192.168.50.138";			// default receiver hostname
static char *port = "8080";						// default receiver port

//Packet buffers
static Packet sndPacket, rcvPacket;
/*
 * rdt3.0 sender
 */
int main(int argc, char **argv) {
	struct sockaddr_in peer;
	int fd, s;
	unsigned char sndSeq;
	Event event;
	State state;
	Packet *sndpkt = &sndPacket;
	Packet *rcvpkt = &rcvPacket;
	char *data = &(sndPacket.data[0]);
	char *hostname, *service;
	int opt;

	// Command line option processing
	while ((opt = getopt(argc, argv, "dt:")) != -1) {
		switch (opt) {
		case 'd':
			DEBUG_ON = 1; 	// Debug mode
			break;
		case 't':
			ReTxTimeout = atoi(optarg);
			break;
		default:
			error(1, 0,
					"Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n",
					argv[0]);
		}
	}
	if (optind == argc - 2) {
		hostname = argv[optind];
		service = argv[optind + 1];
	} else if (optind == argc) {
		hostname = localhost;
		service = port;
	} else
		error(1, 0,
				"Usage: %s [-d] [-t timeout] [<receiver name or IP address> <port number>]\n",
				argv[0]);
	debug("To %s:%s\tTimeout = %dmsec\tDebugMode = %s\n", hostname, service,
			ReTxTimeout, DEBUG_ON ? "on" : "off");

	set_address(hostname, service, &peer, "udp"); // Set peer's IP and port

	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
		error(1, errno, "socket creation failed\n");

	fd = fileno(stdin);

	// Init conditiion
	sndSeq = 0;
	state = Wait_for_call;

	// FSM
	for (;;) {
		int datalen, rcvlen;

		event = getEvent(fd, s);
		// Actions taken by State and Event
		switch (state) {
		// Wait_for_call state
		case Wait_for_call:
			switch (event) {
			case RDT_SEND:
				if (fgets(data, MAX_DATA, stdin) != NULL ) { // Get data from above
					sndpkt = make_pkt(DATA, sndSeq++, sndpkt, sizeof(data));
					udt_send(s, sndpkt, (struct sockadd *) &peer,
							sizeof(peer));
//					state = Wait_for_ACK;
					continue;
				} else {				// EoF encountered. Close the session
//					state = Closing;
//					close(s);
					continue;
				}
			default:
				continue;				// ignore the event
			}

			//Wait_for_ACK state
		case Wait_for_ACK:
			switch (event) {
			case RDT_RCV:
				rcvlen = sizeof(peer);
				recvfrom (s, rcvpkt, sizeof(rcvpkt), 0, (struct sockaddr *) &peer, (socklen_t *) &rcvlen);
				if(rcvpkt->header.type == 2){
//					printf("%s", "ACK RECEIVED\n");
				}
				else if(rcvpkt->header.type == 3){
//					state = Closing;
//					close(s);
				}else if(rcvpkt->header.type == 1){

				}else if(rcvpkt->header.type == 0){
//					printf("I HAD NOOP\n");
					sendto(s, sndpkt->data[rcvpkt->header.seq] , sizeof(sndpkt->data[rcvpkt->header.seq]), 0, (struct sockadd *) &peer,
											sizeof(peer));
				}
				/*
				 * More codes here ...
				 */
				state = Wait_for_call;

			case TIMEOUT:
				sendto(s, rcvPacket.data , MAX_DATA, 0, (struct sockadd *) &peer,
															sizeof(peer));
				/*
				 * More codes here ...
				 */
				continue;
			default:
				continue;
			}
			// Closing state
			// wait an ACK. Sender shall stop when anything hear from receiver
		case Closing:
			switch (event) {
			case RDT_RCV:
				/*
				 * More codes here ...
				 */
			case TIMEOUT:
				/*
				 * More codes here ...
				 */
				continue;
			default:
				continue;
			}	// End of inner switch
		}	// End of outer switch

	}   // End of for loop

}	// End of main

